package io.kiki.sba.registry.server.metadata.bootstrap;

import io.kiki.sba.registry.api.exchange.DataExchanger;
import io.kiki.sba.registry.remoting.http.exchange.HttpExchanger;
import io.kiki.sba.registry.remoting.netty.NettyDataExchanger;
import io.kiki.sba.registry.server.metadata.bootstrap.config.*;
import io.kiki.sba.registry.server.metadata.cleaner.AppRevisionCleaner;
import io.kiki.sba.registry.server.metadata.cleaner.InterfaceAppsIndexCleaner;
import io.kiki.sba.registry.server.metadata.lease.filter.ForbiddenServerManagerImpl;
import io.kiki.sba.registry.server.metadata.lease.filter.RegistryForbiddenServerManager;
import io.kiki.sba.registry.server.metadata.provide.data.*;
import io.kiki.sba.registry.server.metadata.remoting.DataNodeExchanger;
import io.kiki.sba.registry.server.metadata.remoting.MetaServerExchanger;
import io.kiki.sba.registry.server.metadata.remoting.SessionNodeExchanger;
import io.kiki.sba.registry.server.metadata.remoting.connection.ClientInterfaceConnectionManager;
import io.kiki.sba.registry.server.metadata.remoting.connection.DataStoreConnectionManager;
import io.kiki.sba.registry.server.metadata.remoting.connection.MetaConnectionManager;
import io.kiki.sba.registry.server.metadata.remoting.handler.*;
import io.kiki.sba.registry.server.metadata.remoting.meta.LocalCentralControlExchanger;
import io.kiki.sba.registry.server.metadata.remoting.meta.MetaServerRenewService;
import io.kiki.sba.registry.server.metadata.resource.*;
import io.kiki.sba.registry.server.metadata.resource.filter.AuthRestFilter;
import io.kiki.sba.registry.server.metadata.resource.filter.LeaderAwareFilter;
import io.kiki.sba.registry.server.metadata.slot.status.SlotsStatusService;
import io.kiki.sba.registry.server.shared.config.CommonConfig;
import io.kiki.sba.registry.server.shared.remoting.AbstractServerHandler;
import io.kiki.sba.registry.server.shared.resource.MetricsResource;
import io.kiki.sba.registry.server.shared.resource.SlotGenericResource;
import io.kiki.sba.registry.server.shared.resource.VersionResource;
import io.kiki.sba.registry.store.api.config.StoreApiConfiguration;
import io.kiki.sba.registry.store.jdbc.config.JdbcConfiguration;
import io.kiki.sba.registry.store.jdbc.config.JdbcElectorConfiguration;
import io.kiki.sba.registry.task.MetricsableThreadPoolExecutor;
import io.kiki.sba.registry.util.DefaultExecutorFactory;
import io.kiki.sba.registry.util.NamedThreadFactory;
import io.kiki.sba.registry.util.OsUtils;
import io.kiki.sba.registry.util.PropertySplitter;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.*;


@Configuration
@Import({MetaServerInitializerConfiguration.class, StoreApiConfiguration.class, JdbcConfiguration.class, JdbcElectorConfiguration.class})
@EnableConfigurationProperties
public class MetaServerConfiguration {

    public static final String SHARED_SCHEDULE_EXECUTOR = "sharedScheduleExecutor";
    public static final String GLOBAL_EXECUTOR = "globalExecutor";

    @Bean
    @ConditionalOnMissingBean
    public MetaServerBootstrap metaServerBootstrap() {
        return new MetaServerBootstrap();
    }

    @Configuration
    protected static class MetaServerConfigBeanConfiguration {

        @Bean
        public CommonConfig commonConfig() {
            return new CommonConfig();
        }

        @Bean
        @ConditionalOnMissingBean
        public ServerConfig metaServerConfig(CommonConfig commonConfig) {
            return new ServerConfigBean(commonConfig);
        }

        @Bean
        public NodeConfig nodeConfig() {
            return new NodeConfigBeanProperty();
        }

        @Bean(name = "PropertySplitter")
        public PropertySplitter propertySplitter() {
            return new PropertySplitter();
        }
    }

    @Configuration
    public static class ThreadPoolResourceConfiguration {
        @Bean(name = GLOBAL_EXECUTOR)
        public ExecutorService getGlobalExecutorService() {
            int corePoolSize = Math.min(OsUtils.getCpuCount() * 2, 8);
            int maxPoolSize = 50 * OsUtils.getCpuCount();
            DefaultExecutorFactory executorFactory = DefaultExecutorFactory.builder().threadNamePrefix(GLOBAL_EXECUTOR).corePoolSize(corePoolSize).maxPoolSize(maxPoolSize).rejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()).build();
            return executorFactory.create();
        }

        @Bean(name = SHARED_SCHEDULE_EXECUTOR)
        public ScheduledExecutorService getScheduledService() {
            return new ScheduledThreadPoolExecutor(Math.min(OsUtils.getCpuCount() * 2, 12), new NamedThreadFactory("MetaServerGlobalScheduler"));
        }
    }

    @Configuration
    public static class MetaServerRemotingConfiguration {

        @Bean
        public DataExchanger boltExchange() {
            return new NettyDataExchanger();
        }

        @Bean
        public DataExchanger jerseyExchange() {
            return new HttpExchanger();
        }

        @Bean(name = "sessionServerHandlers")
        public Collection<AbstractServerHandler> sessionServerHandlers() {
            Collection<AbstractServerHandler> list = new ArrayList<>();
            list.add(heartbeatRequestHandler());
            list.add(fetchProvideDataRequestHandler());
            list.add(fetchSystemPropertyRequestHandler());
            list.add(registryForbiddenServerHandler());
            list.add(getSlotsStatusRequestHandler());
            return list;
        }

        @Bean(name = "dataServerHandlers")
        public Collection<AbstractServerHandler> dataServerHandlers() {
            Collection<AbstractServerHandler> list = new ArrayList<>();
            list.add(heartbeatRequestHandler());
            list.add(fetchProvideDataRequestHandler());
            list.add(registryForbiddenServerHandler());
            list.add(fetchSystemPropertyRequestHandler());
            list.add(getSlotsStatusRequestHandler());
            return list;
        }

        @Bean(name = "metaServerHandlers")
        public Collection<AbstractServerHandler> metaServerHandlers() {
            Collection<AbstractServerHandler> list = new ArrayList<>();
            list.add(heartbeatRequestHandler());
            return list;
        }

        @Bean
        public ClientInterfaceConnectionManager sessionConnectionManager() {
            return new ClientInterfaceConnectionManager();
        }

        @Bean
        public DataStoreConnectionManager dataConnectionManager() {
            return new DataStoreConnectionManager();
        }

        @Bean
        public MetaConnectionManager metaConnectionManager() {
            return new MetaConnectionManager();
        }

        @Bean
        public HeartbeatRequestHandler heartbeatRequestHandler() {
            return new HeartbeatRequestHandler();
        }

        @Bean
        public GetSlotsStatusRequestHandler getSlotsStatusRequestHandler() {
            return new GetSlotsStatusRequestHandler();
        }

        @Bean
        public FetchProvideDataRequestHandler fetchProvideDataRequestHandler() {
            return new FetchProvideDataRequestHandler();
        }

        @Bean
        public FetchSystemPropertyRequestHandler fetchSystemPropertyRequestHandler() {
            return new FetchSystemPropertyRequestHandler();
        }

        @Bean
        public SessionNodeExchanger sessionNodeExchanger() {
            return new SessionNodeExchanger();
        }

        @Bean
        public DataNodeExchanger dataNodeExchanger() {
            return new DataNodeExchanger();
        }

        @Bean
        public MetaServerExchanger metaServerExchanger() {
            return new MetaServerExchanger();
        }

        @Bean
        public RegistryForbiddenServerHandler registryForbiddenServerHandler() {
            return new RegistryForbiddenServerHandler();
        }

        @Bean
        public LocalCentralControlExchanger localMetaExchanger() {
            return new LocalCentralControlExchanger();
        }

        @Bean
        public MetaServerRenewService metaServerRenewService() {
            return new MetaServerRenewService();
        }

        @Bean
        public SlotsStatusService SlotsStatusService() {
            return new SlotsStatusService();
        }

        @Bean
        @ConditionalOnMissingBean(name = "registryForbiddenServerManager")
        public RegistryForbiddenServerManager registryForbiddenServerManager() {
            return new ForbiddenServerManagerImpl();
        }
    }

    @Configuration
    public static class MetadataConfiguration {

        @Bean
        public ProvideDataService provideDataService() {
            return new DefaultProvideDataService();
        }

        @Bean
        public NodeOperatingService nodeOperatingService() {
            return new NodeOperatingService();
        }

        @Bean
        public DefaultClientManagerService clientManagerService() {
            return new DefaultClientManagerService();
        }

        @Bean
        public AppRevisionCleaner appRevisionCleaner() {
            return new AppRevisionCleaner();
        }

        @Bean
        @ConditionalOnMissingBean
        public InterfaceAppsIndexCleaner interfaceAppsIndexCleaner() {
            return new InterfaceAppsIndexCleaner();
        }

        @Bean
        public FetchStopPushService fetchStopPushService() {
            return new FetchStopPushService();
        }
    }

    @Configuration
    public static class ResourceConfiguration {

        @Bean
        public ResourceConfig jerseyResourceConfig() {
            ResourceConfig resourceConfig = new ResourceConfig();
            resourceConfig.register(JacksonFeature.class);
            return resourceConfig;
        }

        @Bean
        public LeaderAwareFilter leaderAwareFilter() {
            return new LeaderAwareFilter();
        }

        @Bean
        public AuthRestFilter authRestFilter() {
            return new AuthRestFilter();
        }

        @Bean
        public ProvideDataResource provideDataResource() {
            return new ProvideDataResource();
        }

        @Bean
        public MetaDigestResource metaDigestResource() {
            return new MetaDigestResource();
        }

        @Bean
        public HealthResource healthResource() {
            return new HealthResource();
        }

        @Bean
        public MetaLeaderResource metaLeaderResource() {
            return new MetaLeaderResource();
        }

        @Bean
        @ConditionalOnMissingBean
        public StopPushDataResource stopPushDataResource() {
            return new StopPushDataResource();
        }

        @Bean
        public ShutdownSwitchResource shutdownSwitchResource() {
            return new ShutdownSwitchResource();
        }

        @Bean
        @ConditionalOnMissingBean
        public VersionResource versionResource() {
            return new VersionResource();
        }

        @Bean
        public CompressResource compressedResource() {
            return new CompressResource();
        }

        @Bean
        @ConditionalOnMissingBean
        public MetaCenterResource cleanerResource() {
            return new MetaCenterResource();
        }

        @Bean
        public BlacklistDataResource blacklistDataResource() {
            return new BlacklistDataResource();
        }

        @Bean
        public ClientManagerResource clientManagerResource() {
            return new ClientManagerResource();
        }

        @Bean
        public CircuitBreakerResources circuitBreakerResources() {
            return new CircuitBreakerResources();
        }

        @Bean
        public RecoverConfigResource recoverConfigResource() {
            return new RecoverConfigResource();
        }

        @Bean
        public SlotSyncResource renewSwitchResource() {
            return new SlotSyncResource();
        }

        @Bean
        public SlotsResource SlotsResource() {
            return new SlotsResource();
        }

        @Bean
        public SlotGenericResource slotResource() {
            return new SlotGenericResource();
        }

        @Bean
        public MetricsResource metricsResource() {
            return new MetricsResource();
        }

        @Bean
        public RegistryCoreOpsResource registryCoreOpsResource() {
            return new RegistryCoreOpsResource();
        }
    }

    @Configuration
    public static class ExecutorConfiguation {

        @Bean
        public ThreadPoolExecutor defaultRequestExecutor(ServerConfig serverConfig) {
            ThreadPoolExecutor defaultRequestExecutor = new MetricsableThreadPoolExecutor("MetaHandlerDefaultExecutor", serverConfig.getDefaultRequestExecutorMinSize(), serverConfig.getDefaultRequestExecutorMaxSize(), 300, TimeUnit.SECONDS, new LinkedBlockingQueue<>(serverConfig.getDefaultRequestExecutorQueueSize()), new NamedThreadFactory("MetaHandler-DefaultRequest"));
            defaultRequestExecutor.allowCoreThreadTimeOut(true);
            return defaultRequestExecutor;
        }

        @Bean
        public ExecutorManager executorManager(ServerConfig serverConfig, MultiClusterMetaServerConfig multiClusterMetaServerConfig) {
            return new ExecutorManager(serverConfig, multiClusterMetaServerConfig);
        }
    }
}
